import { describe, test } from "bun:test";
import assert from "node:assert";
import path from "node:path";

describe("path.join", () => {
  test("general", () => {
    const failures = [];
    const backslashRE = /\\/g;

    const joinTests = [
      [
        [path.posix.join, path.win32.join],
        // Arguments                     result
        [
          [[".", "x/b", "..", "/b/c.js"], "x/b/c.js"],
          [[], "."],
          [["/.", "x/b", "..", "/b/c.js"], "/x/b/c.js"],
          [["/foo", "../../../bar"], "/bar"],
          [["foo", "../../../bar"], "../../bar"],
          [["foo/", "../../../bar"], "../../bar"],
          [["foo/x", "../../../bar"], "../bar"],
          [["foo/x", "./bar"], "foo/x/bar"],
          [["foo/x/", "./bar"], "foo/x/bar"],
          [["foo/x/", ".", "bar"], "foo/x/bar"],
          [["./"], "./"],
          [[".", "./"], "./"],
          [[".", ".", "."], "."],
          [[".", "./", "."], "."],
          [[".", "/./", "."], "."],
          [[".", "/////./", "."], "."],
          [["."], "."],
          [["", "."], "."],
          [["", "foo"], "foo"],
          [["foo", "/bar"], "foo/bar"],
          [["", "/foo"], "/foo"],
          [["", "", "/foo"], "/foo"],
          [["", "", "foo"], "foo"],
          [["foo", ""], "foo"],
          [["foo/", ""], "foo/"],
          [["foo", "", "/bar"], "foo/bar"],
          [["./", "..", "/foo"], "../foo"],
          [["./", "..", "..", "/foo"], "../../foo"],
          [[".", "..", "..", "/foo"], "../../foo"],
          [["", "..", "..", "/foo"], "../../foo"],
          [["/"], "/"],
          [["/", "."], "/"],
          [["/", ".."], "/"],
          [["/", "..", ".."], "/"],
          [[""], "."],
          [["", ""], "."],
          [[" /foo"], " /foo"],
          [[" ", "foo"], " /foo"],
          [[" ", "."], " "],
          [[" ", "/"], " /"],
          [[" ", ""], " "],
          [["/", "foo"], "/foo"],
          [["/", "/foo"], "/foo"],
          [["/", "//foo"], "/foo"],
          [["/", "", "/foo"], "/foo"],
          [["", "/", "foo"], "/foo"],
          [["", "/", "/foo"], "/foo"],
        ],
      ],
    ];

    // Windows-specific join tests
    joinTests.push([
      path.win32.join,
      joinTests[0][1].slice(0).concat([
        // Arguments                     result
        // UNC path expected
        [["//foo/bar"], "\\\\foo\\bar\\"],
        [["\\/foo/bar"], "\\\\foo\\bar\\"],
        [["\\\\foo/bar"], "\\\\foo\\bar\\"],
        // UNC path expected - server and share separate
        [["//foo", "bar"], "\\\\foo\\bar\\"],
        [["//foo/", "bar"], "\\\\foo\\bar\\"],
        [["//foo", "/bar"], "\\\\foo\\bar\\"],
        // UNC path expected - questionable
        [["//foo", "", "bar"], "\\\\foo\\bar\\"],
        [["//foo/", "", "bar"], "\\\\foo\\bar\\"],
        [["//foo/", "", "/bar"], "\\\\foo\\bar\\"],
        // UNC path expected - even more questionable
        [["", "//foo", "bar"], "\\\\foo\\bar\\"],
        [["", "//foo/", "bar"], "\\\\foo\\bar\\"],
        [["", "//foo/", "/bar"], "\\\\foo\\bar\\"],
        // No UNC path expected (no double slash in first component)
        [["\\", "foo/bar"], "\\foo\\bar"],
        [["\\", "/foo/bar"], "\\foo\\bar"],
        [["", "/", "/foo/bar"], "\\foo\\bar"],
        // No UNC path expected (no non-slashes in first component -
        // questionable)
        [["//", "foo/bar"], "\\foo\\bar"],
        [["//", "/foo/bar"], "\\foo\\bar"],
        [["\\\\", "/", "/foo/bar"], "\\foo\\bar"],
        [["//"], "\\"],
        // No UNC path expected (share name missing - questionable).
        [["//foo"], "\\foo"],
        [["//foo/"], "\\foo\\"],
        [["//foo", "/"], "\\foo\\"],
        [["//foo", "", "/"], "\\foo\\"],
        // No UNC path expected (too many leading slashes - questionable)
        [["///foo/bar"], "\\foo\\bar"],
        [["////foo", "bar"], "\\foo\\bar"],
        [["\\\\\\/foo/bar"], "\\foo\\bar"],
        // Drive-relative vs drive-absolute paths. This merely describes the
        // status quo, rather than being obviously right
        [["c:"], "c:."],
        [["c:."], "c:."],
        [["c:", ""], "c:."],
        [["", "c:"], "c:."],
        [["c:.", "/"], "c:.\\"],
        [["c:.", "file"], "c:file"],
        [["c:", "/"], "c:\\"],
        [["c:", "file"], "c:\\file"],
      ]),
    ]);
    joinTests.forEach(test => {
      if (!Array.isArray(test[0])) test[0] = [test[0]];
      test[0].forEach(join => {
        test[1].forEach(test => {
          const actual = join.apply(null, test[0]);
          const expected = test[1];
          // For non-Windows specific tests with the Windows join(), we need to try
          // replacing the slashes since the non-Windows specific tests' `expected`
          // use forward slashes
          let actualAlt;
          let os;
          if (join === path.win32.join) {
            actualAlt = actual.replace(backslashRE, "/");
            os = "win32";
          } else {
            os = "posix";
          }
          if (actual !== expected && actualAlt !== expected) {
            const delimiter = test[0].map(JSON.stringify).join(",");
            const message = `path.${os}.join(${delimiter})\n  expect=${JSON.stringify(
              expected,
            )}\n  actual=${JSON.stringify(actual)}`;
            failures.push(`\n${message}`);
          }
        });
      });
    });

    assert.strictEqual(failures.length, 0, failures.join(""));
  });
});
